import React from 'react';
import PropTypes from 'prop-types';

import { uniqueAnimate, clearUniqueAnimate } from '../utils/animateUtils';

let animationId = 0;
const NUMBER_MATCH = /^\s*(-?\d*(\.\d+)?)\s*(px|%)?\s*$/;

function stepValue(value = '', nextValue = '', multiple, precision) {
	const numMultiple = Number(multiple);
	const myMultiple = !(numMultiple > 1) ? 2 : numMultiple;
	const numPrecision = Number(precision);
	const myPrecision = !(numPrecision > 0.00001) ? 0.01 : numPrecision;

	// Number
	if (typeof value === 'number') {
		let stepNum = value + ((nextValue - value) / myMultiple);
		if (Math.abs(stepNum - nextValue) < myPrecision) {
			stepNum = nextValue;
		}
		return stepNum;
	}

	// Number unit
	const numberMatch = value.match(NUMBER_MATCH);
	const nextNumberMatch = nextValue.match(NUMBER_MATCH);

	if (!numberMatch !== !nextNumberMatch) {
		return nextValue;
	}

	if (numberMatch) {
		const num = Number(numberMatch[1]);
		const nextNum = Number(nextNumberMatch[1]);
		let stepNum = num + ((nextNum - num) / myMultiple);
		stepNum = Math.floor(stepNum * 100) / 100;
		if (Math.abs(stepNum - nextNum) < myPrecision) {
			stepNum = nextNum;
		}
		return `${stepNum}${nextNumberMatch[3] || ''}`;
	}

	return nextValue;
}

class Animation extends React.Component {
	constructor() {
		super();
		this.state = {};

		this.animationId = animationId;
		animationId += 1;
	}

	componentWillMount() {
		this.setState({ style: this.props.style });
	}

	componentWillReceiveProps(nextProps) {
		const { style: originStyle } = this.state;
		const { style: nextStyle, multiple, precision } = nextProps;
		const keys = new Set([...Object.keys(originStyle), ...Object.keys(nextStyle)]);

		uniqueAnimate(`Animation_${this.animationId}`, () => {
			let changed = false;
			const { style } = this.state;
			const stepStyle = Object.assign({}, style);

			keys.forEach((key) => {
				const value = style[key];
				const nextValue = nextStyle[key];

				if (value === undefined || nextValue === undefined) {
					stepStyle[key] = nextValue;
					changed = true;
				} else if (typeof value !== typeof nextValue) {
					stepStyle[key] = nextValue;
					changed = true;
				} else if (value !== nextValue) {
					stepStyle[key] = stepValue(value, nextValue, multiple, precision);
					changed = true;
				}
			});

			if (changed) {
				this.setState({ style: stepStyle });
			}
			return changed;
		});
	}

	componentWillUnmount() {
		clearUniqueAnimate(`Animation_${this.animationId}`);
	}

	render() {
		const { children } = this.props;
		const { style } = this.state;

		if (typeof children !== 'function') return <span>BDP Animation require function children</span>;
		return children(style);
	}
}

Animation.propTypes = {
	style: PropTypes.object,
	children: PropTypes.func,
	multiple: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
	precision: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};

export default Animation;
